/**
 * Copyright 2010 VTT Finnish Technical Research Centre
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

package org.osami.syncagent.impl;

import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.UnknownHostException;
import java.util.Calendar;
import java.util.Dictionary;
import java.util.Hashtable;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.osami.syncagent.SyncAgentService;
import org.osami.syncagent.server.SyncAgentServer;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.ServiceRegistration;
import org.osgi.service.event.EventAdmin;
import org.osgi.service.event.EventConstants;
import org.osgi.service.event.EventHandler;
import org.osgi.util.tracker.ServiceTracker;

public class Activator implements BundleActivator {
		private ServiceRegistration reg;
		private ServiceTracker tracker;
	 	private Map<SyncAgentService, String> agents = new ConcurrentHashMap<SyncAgentService, String>();
	 	private EventAdmin localAdmin;
		private SyncAgentServer syncServer;
		private SyncAgentService localSyncAgent;

		public static final int BC_PORT = 4443;
		public static final int SERVER_PORT = 4444;
		
		private static BundleContext bc;
		
		public static BundleContext getBundleContext() {
			return bc;
		}		
		
		public void start(BundleContext context) {
	
			bc = context;
			// Register service as a distributed OSGi service
			Dictionary props = new Hashtable();
	        
	        String host = getHostName(); // obtain the current host name
	        int port = 0;
			try {
				port = getPort();
			} catch (IOException e) {
				System.out.println("SyncAgent: Unable to get port");
			}        // find a free port
	        
	        props.put("service.exported.interfaces", "*");
	        props.put("service.exported.configs", "org.apache.cxf.ws");
	        props.put("org.apache.cxf.ws.address", getAddress(host, port)); // old obsolete value
	        props.put("endpoint.id", getAddress(host, port));
	        
	        Calendar calendar = Calendar.getInstance();
			java.util.Date now = calendar.getTime();
			localSyncAgent = (SyncAgentService) new SyncAgentServiceImpl(now.toString());
			
			((SyncAgentServiceImpl)localSyncAgent).setActivatorRef(this);
			
			reg = context.registerService(SyncAgentService.class.getName(), localSyncAgent, props);
			
			// Register Event Handler to receive event from Event Admin
			context.registerService(EventHandler.class.getName(),  
									new SyncAgentEventHandler(localSyncAgent),  
									getHandlerServiceProperties(getEventFilter()));
			
			getRemoteAgents(context);
			
			//Get reference to local EventAdmin where remote events are publisher
			ServiceReference refs = context.getServiceReference(EventAdmin.class.getName());
			if (refs == null) {
				System.out.println("EventAdmin unavailable on SyncAgent start");
			} 
			else {
				localAdmin = (EventAdmin) context.getService(refs);
			}
			System.out.println("SyncAgentActivator: Now starting non-OSGi 4.2 comliant servers");
			/**
			 * Open TCP/IP Server socket for listening non OSGi 4.2 nodes
			 */
			syncServer =  new SyncAgentServer(localSyncAgent);
				        
	        //System.out.println("SyncAgentActivator: Server thread started, send broadcast message");
	        //syncServer.sendBroadCastMessage();		
			
		}

		

		/**
		 * Get Filter to which OSGI events are listened. This should read from conf
		 * File eventually, but now we just go with org/osami/
		 * @return
		 */
		private String getEventFilter() {
			String filter = "org/osami/*";
			return filter;
		}
		/**
		 * This is perhaps redundant with above method and could be in-lined. 
		 * @param topics
		 * @return
		 */
		protected Dictionary getHandlerServiceProperties(String... topics) {  
			Dictionary result = new Hashtable();  
			result.put(EventConstants.EVENT_TOPIC, topics);  
			return result;  
		}  

		/**
		 * Get remote OSGi agent services
		 * @param context
		 */
		private void getRemoteAgents(BundleContext context) {
			// Get instances of remote OSGi SyncAgent Services
			tracker = new ServiceTracker(context, SyncAgentService.class.getName(), null) {
	            public Object addingService(ServiceReference reference) {
	                Object svc = super.addingService(reference);
	                if (svc instanceof SyncAgentService) {
	                	SyncAgentService d = (SyncAgentService) svc;
	                	// Do not add local service to the list comparison should be done 
	                	// better than String comparison i.e. the ID Should be MAC address
	                	if (d.getID().compareTo(localSyncAgent.getID()) != 0)
	                	{
	                		System.out.println("Adding agent: " + d.getID() + " (" + d + ")");
	                		agents.put(d, d.getID());
	                	}
	                }
	                return svc;
	            }

	            public void removedService(ServiceReference reference, Object service) {
	                String value = agents.remove(service);
	                System.out.println("Removed agent: " + value);
	                super.removedService(reference, service);
	            }            
	        };        
	        tracker.open();
		}

		public void stop(BundleContext context) {
			// need to free sockets and unregister TCP/IP clients. 
			reg.unregister();
			syncServer.shutdown();
		}

		/**
		 * This is for remote OSGi. Get Hashtable of remote OSGi service instances
		 * @return
		 */
		public Map<SyncAgentService, String> getClients() {
			return agents;
		}
		
		/**
		 * This is for remote OSGi. Not for TCP/IP server
		 * @return
		 */
		private static String getAddress(String host, int port) {        
	        return "http://" + host + ":" + port + "/syncagent";
	    }

		/**
		 * This is for remote OSGi. Not for TCP/IP server
		 * @return
		 */
	    private static String getHostName() {
	        try {
	            //return InetAddress.getLocalHost().getCanonicalHostName();
	        	return InetAddress.getLocalHost().getHostAddress();
	        } catch (UnknownHostException e) {
	            return "localhost";
	        }
	    }

		
	    private static int getPort() throws IOException {
	        return new ServerSocket(0).getLocalPort();
	    }

		public EventAdmin getEventAdmin() {
				return localAdmin;
		}

		/**
		 * Push message to non OSGi 4.2 clients.
		 * @param message
		 */
		public void pushToClients(String message) {
			System.out.println("Activator: calling SyncAgentServer pushEvents method");
			syncServer.pushEvents(message);
		}
}